package com.coflxl.web.aop;

import cn.hutool.json.JSONUtil;
import com.coflxl.web.api.common.model.SysCommonLog;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.sagacity.sqltoy.dao.LightDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Aspect
@Component
public class EnhancedRequestLogAspect {

    @Autowired
    private LightDao logRepository;

    // 切点：所有Controller方法
    @Pointcut("@within(org.springframework.stereotype.Controller) || " +
            "@within(org.springframework.web.bind.annotation.RestController)")
    public void controllerPointcut() {}

    @Around("controllerPointcut()")
    public Object logApiRequest(ProceedingJoinPoint joinPoint) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
                .getRequest();

        // 创建日志对象
        SysCommonLog requestLog = new SysCommonLog();
        requestLog.setCreateTime(LocalDateTime.now());
        requestLog.setClientIp(getClientIp(request));
        requestLog.setHttpMethod(request.getMethod());
        requestLog.setRequestUri(request.getRequestURI());
        requestLog.setUserAgent(request.getHeader("User-Agent"));

        // 记录请求参数
        recordRequestParameters(joinPoint, request, requestLog);

        long startTime = System.currentTimeMillis();
        Object result = null;

        try {
            result = joinPoint.proceed();
            requestLog.setStatusCode("200"); // 默认成功

            // 记录响应
            if (result != null) {
                requestLog.setResponseBody(JSONUtil.parse(result).toString());
            }
            return result;
        } catch (Exception e) {
            requestLog.setStatusCode("500");
            requestLog.setResponseBody("Error: " + e.getMessage());
            throw e;
        } finally {
            requestLog.setExecutionTimeMs(System.currentTimeMillis() - startTime);
            saveLogAsync(requestLog);  // 异步保存
        }
    }

    /**
     * 记录各种类型的请求参数
     */
    private void recordRequestParameters(ProceedingJoinPoint joinPoint, HttpServletRequest request,
                                         SysCommonLog requestLog) {
        // 1. 记录查询参数(GET)
        if (request.getQueryString() != null) {
            requestLog.setQueryParams(request.getQueryString());
        }

        // 2. 记录路径参数
        Map<String, String> pathVariables = getPathVariables(joinPoint, request);
        if (!pathVariables.isEmpty()) {
            requestLog.setPathParams(mapToString(pathVariables));
        }

        // 3. 记录请求体(POST/PUT)
        if (isRequestBodyRequest(request)) {
            Object[] args = joinPoint.getArgs();
            if (args != null && args.length > 0) {
                // 查找@RequestBody参数
                Optional<Object> requestBody = Arrays.stream(args)
                        .filter(arg -> {
                            Parameter[] parameters = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameters();
                            for (int i = 0; i < parameters.length; i++) {
                                if (parameters[i].isAnnotationPresent(RequestBody.class) && args[i] == arg) {
                                    return true;
                                }
                            }
                            return false;
                        })
                        .findFirst();

                requestBody.ifPresent(body -> requestLog.setRequestBody(JSONUtil.parse(body).toString()));
            }
        }
    }

    /**
     * 获取路径变量
     */
    private Map<String, String> getPathVariables(ProceedingJoinPoint joinPoint, HttpServletRequest request) {
        Map<String, String> pathVariables = new HashMap<>();

        // 获取方法上的路径变量名
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        String[] paramNames = Arrays.stream(method.getParameters())
                .filter(p -> p.isAnnotationPresent(PathVariable.class))
                .map(p -> {
                    PathVariable pathVar = p.getAnnotation(PathVariable.class);
                    return pathVar.value().isEmpty() ? p.getName() : pathVar.value();
                })
                .toArray(String[]::new);

        // 从请求属性中获取路径变量值
        if (paramNames.length > 0) {
            @SuppressWarnings("unchecked")
            Map<String, String> uriTemplateVars = (Map<String, String>) request
                    .getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);

            if (uriTemplateVars != null) {
                uriTemplateVars.forEach((name, value) -> {
                    if (Arrays.asList(paramNames).contains(name)) {
                        pathVariables.put(name, value);
                    }
                });
            }
        }

        return pathVariables;
    }

    /**
     * 判断是否是包含请求体的请求
     */
    private boolean isRequestBodyRequest(HttpServletRequest request) {
        return "POST".equalsIgnoreCase(request.getMethod()) ||
                "PUT".equalsIgnoreCase(request.getMethod()) ||
                "PATCH".equalsIgnoreCase(request.getMethod());
    }

    /**
     * 异步保存日志
     */
    @Async
    public void saveLogAsync(SysCommonLog requestLog) {
        try {
            // 对过长的内容进行截断
            if (requestLog.getRequestBody() != null && requestLog.getRequestBody().length() > 2000) {
                requestLog.setRequestBody(requestLog.getRequestBody().substring(0, 2000) + "...[TRUNCATED]");
            }
            if (requestLog.getResponseBody() != null && requestLog.getResponseBody().length() > 2000) {
                requestLog.setResponseBody(requestLog.getResponseBody().substring(0, 2000) + "...[TRUNCATED]");
            }
            logRepository.save(requestLog);
        } catch (Exception e) {
            // 日志记录失败不应影响主流程
            System.err.println("Failed to save request log: " + e.getMessage());
        }
    }

    // 辅助方法
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    private String mapToString(Map<?, ?> map) {
        return map.entrySet().stream()
                .map(e -> e.getKey() + "=" + e.getValue())
                .collect(Collectors.joining(", "));
    }

}